﻿using In66.Net.EventBus.RabbitMq.Abstraction;
using In66.Net.EventBus.RabbitMq.Configs;
using In66.Net.EventBus.RabbitMq.IIntegration;
using In66.Net.EventBus.RabbitMq.Manager;
using Microsoft.Extensions.DependencyInjection;

namespace In66.Net.EventBus.Extensions
{
    public static class ServiceCollectionExtension
    {
        private static readonly ConcurrentDictionary<string, char> s_RegisteredModels = new();

        public static bool HasRegistered(this IServiceCollection _, string modelName) => !s_RegisteredModels.TryAdd(modelName.ToLower(), '1');

        #region Cap
        public static IServiceCollection AddInfraCap<TSubscriber>(this IServiceCollection services, Action<CapOptions> setupAction)
            where TSubscriber : class, ICapSubscribe
        {
            if (services.HasRegistered(nameof(AddInfraCap)))
            {
                return services;
            }

            services
                .AddSingleton<IEventPublisher, CapPublisher>()
                .AddScoped<TSubscriber>()
                .AddCap(setupAction)
                .AddSubscribeFilter<DefaultCapFilter>();
            return services;
        }
        #endregion

        #region Rabbit
        /// <summary>
        /// 添加消息总线RabbitMQ服务(集群模式)
        /// </summary>
        /// <param name="service"></param>
        /// <param name="action"></param>
        public static IServiceCollection AddRabbitBus(this IServiceCollection service, Action<RabbitMultiConfig>? action = null)
        {
            RabbitMultiConfig config = new();
            action?.Invoke(config);
            service.RabbitPersistentConnection(config).AddIntegrationEventBus(config.RetryCount);
            return service;
        }

        /// <summary>
        /// 添加消息总线RabbitMQ服务(单节点模式)
        /// </summary>
        /// <param name="service"></param>
        /// <param name="action"></param>
        public static IServiceCollection AddRabbitBus(this IServiceCollection service, Action<RabbitSingleConfig>? action = null)
        {
            RabbitSingleConfig config = new();
            action?.Invoke(config);
            service.RabbitPersistentConnection(config).AddIntegrationEventBus(config.RetryCount);

            return service;
        }

        /// <summary>
        /// 添加消息总线RabbitMQ服务(单节点模式)
        /// </summary>
        /// <param name="service"></param>
        /// <param name="config">IConfiguration,从json配置ConnectionString.Rabbit中获取链接</param>
        /// <param name="retry_count">重试次数</param>
        /// <param name="max_channel_count">最大Channel池数量,默认为: 计算机上逻辑处理器的数量</param>
        public static IServiceCollection AddRabbitBus(this IServiceCollection service, IConfiguration config, int retry_count = 5, uint max_channel_count = 0)
        {
            var conn = config.GetConnectionString("Rabbit") ?? throw new("链接字符串不能为空");
            service.AddRabbitBus(conn, retry_count, max_channel_count);

            return service;
        }

        /// <summary>
        /// 添加消息总线RabbitMQ服务
        /// </summary>
        /// <param name="service"></param>
        /// <param name="conn_str">AMQP链接字符串</param>
        /// <param name="retry_count">重试次数</param>
        /// <param name="max_channel_count">最大Channel池数量,默认为: 计算机上逻辑处理器的数量</param>
        public static IServiceCollection AddRabbitBus(this IServiceCollection service, string conn_str, int retry_count = 5, uint max_channel_count = 0)
        {
#if NET7_0_OR_GREATER
        ArgumentException.ThrowIfNullOrEmpty(conn_str, nameof(conn_str));
#else
            if (string.IsNullOrWhiteSpace(conn_str))
            {
                throw new("链接字符串不能为空");
            }
#endif
            service.RabbitPersistentConnection(conn_str, retry_count, max_channel_count).AddIntegrationEventBus(retry_count);

            return service;
        }

        private static IServiceCollection AddIntegrationEventBus(this IServiceCollection service, int retry_count)
        {
            service.AddSingleton<IIntegrationEventBus, IntegrationEventBus>(sp =>
            {
                var rabbitConn = sp.GetRequiredService<IPersistentConnection>();
                var logger = sp.GetRequiredService<ILogger<IntegrationEventBus>>();
                var subsManager = sp.GetRequiredService<SubscriptionsManager>();
                var deadLetterManager = sp.GetRequiredService<DeadLetterSubscriptionsManager>();
                return rabbitConn is null
                           ? throw new(nameof(rabbitConn))
                           : new IntegrationEventBus(rabbitConn, logger, retry_count, subsManager, deadLetterManager, sp);
            })
                   .AddSingleton<SubscriptionsManager>()
                   .AddSingleton<DeadLetterSubscriptionsManager>()
                   .AddHostedService<SubscribeService>();


            return service;
        }

        private static IServiceCollection RabbitPersistentConnection(this IServiceCollection service, RabbitSingleConfig config)
        {
            _ = service.AddSingleton<IPersistentConnection>(sp =>
            {
                var logger = sp.GetRequiredService<ILogger<PersistentConnection>>();
                return new PersistentConnection(new ConnectionFactory
                {
                    HostName = config.Host,
                    UserName = config.UserName,
                    Password = config.PassWord,
                    Port = config.Port,
                    VirtualHost = config.VirtualHost,
                    DispatchConsumersAsync = true
                }, logger, config.RetryCount, config.MaxChannelPoolCount);
            });
            return service;
        }

        private static IServiceCollection RabbitPersistentConnection(this IServiceCollection service, RabbitMultiConfig config)
        {
            if (config.AmqpTcpEndpoints is null || config.AmqpTcpEndpoints.Count is 0)
            {
                throw new ArgumentNullException($"{nameof(config.AmqpTcpEndpoints)}不能为空", nameof(config.AmqpTcpEndpoints));
            }

            _ = service.AddSingleton<IPersistentConnection>(sp =>
            {
                var logger = sp.GetRequiredService<ILogger<PersistentConnection>>();
                return new PersistentConnection(new ConnectionFactory
                {
                    UserName = config.UserName,
                    Password = config.PassWord,
                    VirtualHost = config.VirtualHost,
                    DispatchConsumersAsync = true
                }, logger, config.RetryCount, config.MaxChannelPoolCount, config.AmqpTcpEndpoints);
            });
            return service;
        }

        private static IServiceCollection RabbitPersistentConnection(this IServiceCollection service, string conn, int retry_count, uint maxChannel)
        {
            _ = service.AddSingleton<IPersistentConnection>(sp =>
            {
                var logger = sp.GetRequiredService<ILogger<PersistentConnection>>();
                return new PersistentConnection(new ConnectionFactory
                {
                    Uri = new(conn),
                    DispatchConsumersAsync = true
                }, logger, retry_count, maxChannel);
            });
            return service;
        }
        #endregion

    }
}
